package;

import hxm.monads.OptionMonad;
import massive.munit.Assert;
import hxm.Monad;

class ExampleTest
{
	static var flashSafeIntNull = #if flash9 0 #else null #end;
	
	public function new()
	{
	}
	
	@Before
	public function setUp():Void
	{
		
	}
	
	@After
	public function tearDown():Void
	{
		
	}

	@Test
	public function helloWorld()
	{
		var r = Monad.exec({
			x <= ["Hello World"];
			return x;
		});	
		
		Assert.isTrue(Std.is(r, Array));
		Assert.areEqual(1, r.length);
		Assert.areEqual("Hello World", r.pop());
	}
	
	@Test
	public function arrayListUnification()
	{
		var list = new List();
		list.add(5);
		list.add(10);
						
		var r = Monad.exec( {
			l <= list;
			a <= [1, 2];
			return l * a;
		});
		Assert.isTrue(Std.is(r, List));
		var l = cast(r, List<Dynamic>);
		Assert.areEqual(4, l.length);
		Assert.areEqual(5, l.pop());
		Assert.areEqual(10, l.pop());
		Assert.areEqual(10, l.pop());
		Assert.areEqual(20, l.pop());
	}
	
	@Test
	public function innerInference()
	{
		var r = Monad.exec( {
			array <= Some(["Hello", "World"]);
			inner <= {
					string <= array;
					return string.substr(3);
			}
			return inner;
		});
		
		Assert.isTrue(Std.is(r, Option));
		switch(r)
		{
			case None:
				Assert.fail("Expected Some, got None.");
			case Some(array):
				Assert.isTrue(Std.is(array, Array));
				Assert.areEqual(2, array.length);
				Assert.areEqual("lo", array.shift());
				Assert.areEqual("ld", array.shift());
		}
	}
	
	@Test
	public function guard1()
	{
		var r = Monad.exec( {
			i <= (1...20);
			guard (i & 1 == 0);
			return i;
		});
		
		for (i in 1...10)
		{
			Assert.isTrue(r.hasNext());
			Assert.areEqual(i * 2, r.next());
		}
	}
	
	@Test
	public function guard2()
	{
		var safeDiv = function(top, bottom)
			return Monad.exec( {
				t <= Some(top);
				b <= Some(bottom);
				guard(b != 0);
				return t / b;
			});
		
		Assert.areEqual(None, safeDiv(2, 0));
		switch(safeDiv(2, 1))
		{
			case None:
				Assert.fail("Expected Some, got None.");
			case Some(result):
				Assert.areEqual(2, result);
		}
	}
}